﻿using System;
using System.Collections.Generic;
using System.Linq;
using Fasterflect;
using Kugar.Core.ExtMethod;
using Kugar.Core.PluginFramework.Common;
using Newtonsoft.Json.Linq;

namespace Kugar.Core.PluginFramework
{
    /// <summary>
    /// 插件的基类
    /// </summary>
    public abstract class PluginBase :MarshalByRefObject,  //按引用编组,不允许跨应用程序域序列化,只能使用代理调用
                                      IPlugin
    {
        private bool _isDisposed = false;
        private Dictionary<string, MethodExecuteInfo> _cacheFunc=new Dictionary<string, MethodExecuteInfo>(StringComparer.CurrentCultureIgnoreCase);
        private static Type baseClassType = typeof(PluginBase);
        private static Type objectClassType = typeof(object);

        protected PluginBase()
        {
            Exception error = null;

            try
            {
                OnLoading();
            }
            catch (Exception e)
            {
                error = e;
            }

            if (error==null)
            {
                var methods = this.GetType().GetMethods()
                    .Where(x => x.IsPublic && 
                                ReflectionExt.GetAttribute<IgoneExportAttribute>(x) == null && 
                                MemberInfoExtensions.IsInstance(x) && 
                                x.DeclaringType!= baseClassType &&
                                x.DeclaringType!= objectClassType &&
                                x.DeclaringType!=typeof(MarshalByRefObject) && 
                                !x.Name.StartsWith("get_") &&
                                !x.Name.StartsWith("set_")
                    );
                
                var emptyArgs = new ArgsInfo[0];

                foreach (var methodInfo in methods)
                {
                    var methodName = methodInfo.Name;

                    var attr = methodInfo.GetAttribute<MethodNameAttribute>();

                    if (attr!=null)
                    {
                        methodName = attr.MethodName;
                    }

                    if (_cacheFunc.ContainsKey(methodName))
                    {
                        _cacheFunc.Clear();
                        error=new Exception($"函数:{methodName}出现重复");
                        break;
                    }

                    var parameters=methodInfo.GetParameters();

                    var funcInfo=new MethodExecuteInfo();
                    funcInfo.ExportName = methodName;
                    funcInfo.RealMethodName = methodInfo.Name;

                    var aop = methodInfo.GetCustomAttributes(typeof(MethodFilterAttribute), true);

                    if (aop.HasData())
                    {
                        funcInfo.AOP = aop.Select(x => (MethodFilterAttribute) x).OrderBy(x => x.Order).ToArrayEx();
                    }

                    if (parameters.HasData())
                    {
                        var lst = new ArgsInfo[parameters.Length];

                        for (int i = 0; i < parameters.Length; i++)
                        {
                            var p = parameters[i];

                            lst[i] = new ArgsInfo()
                                     {
                                         ArgName = p.Name,
                                         ArgsType = p.ParameterType,
                                         DefaultValue = p.DefaultValue
                                     };
                        }

                        funcInfo.Args = lst;
                        //_cacheFunc.Add(methodName,lst);
                    }
                    else
                    {
                        funcInfo.Args = emptyArgs;
                        //_cacheFunc.Add(methodName, emptyArgs);
                    }

                    _cacheFunc.Add(methodName, funcInfo);
                }                
            }

            
            if (error!=null)
            {
                OnLoadError(error);

                throw error;
            }
            else
            {
                OnLoaded();
            }
            
        }

        public abstract int Version { get; }

        /// <summary>
        /// 插件名称,必须唯一
        /// </summary>
        public abstract string PluginName { get; }

        void IDisposable.Dispose()
        {
            if (!_isDisposed)
            {
                lock (this)
                {
                    OnDisposing();

                    _cacheFunc.Clear();

                    _isDisposed = true;
                }
            }
        }

        /// <summary>
        /// 插件释放前触发该函数,用于插件释放时,可以释放用户特定的资源
        /// </summary>
        protected virtual void OnDisposing() { }

        /// <summary>
        /// 函数被执行后触发该函数
        /// </summary>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <param name="error"></param>
        /// <returns></returns>
        protected virtual object OnExecuted(string funcName,object[] args, object result,Exception error)
        {
            return result;
        }

        /// <summary>
        /// 函数被执行前触发该函数
        /// </summary>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        protected virtual void OnExecuting(string funcName, params object[] args)
        {
            return;
        }

        /// <summary>
        /// 执行指定名称的函数,一般由插件框架调用,不建议重载该函数
        /// </summary>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public virtual object OnExecuteCoreJson(string funcName, JObject args)
        {
            MethodExecuteInfo methodinfo;

            if (!_cacheFunc.TryGetValue(funcName, out methodinfo))
            {
                throw new MissingMethodException(this.GetType().Name, funcName);
            }

            ArgsInfo[] argsinfo = methodinfo.Args;

            funcName = methodinfo.RealMethodName;

            var lst = new object[argsinfo.Length];

            for (int i = 0; i < argsinfo.Length; i++)
            {
                var argConfig = argsinfo[i];

                if (args!=null && args.TryGetValue(argConfig.ArgName,out var p))
                {
                    lst[i] = p.ToObject(argConfig.ArgsType);
                }
                else
                {
                    if (argConfig.DefaultValue!=null)
                    {
                        lst[i] = argConfig.DefaultValue;
                    }

                    throw new ArgumentException("不存在指定参数",argConfig.ArgName);
                }
            }

            return OnExecutingInternal(funcName, lst, methodinfo);
        }

        /// <summary>
        /// 执行指定名称的函数,一般由插件框架调用,不建议重载该函数
        /// </summary>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public virtual object OnExecuteCore(string funcName, IReadOnlyDictionary<string, object> args=null)
        {
            MethodExecuteInfo methodinfo;
            
            if (!_cacheFunc.TryGetValue(funcName, out methodinfo))
            {
                throw new MissingMethodException(this.GetType().Name, funcName);
            }

            ArgsInfo[] argsinfo = methodinfo.Args;

            funcName = methodinfo.RealMethodName;

            var lst = new object[argsinfo.Length];

            for (int i = 0; i < argsinfo.Length; i++)
            {
                var argConfig = argsinfo[i];

                if (args!=null && args.TryGetValue(argConfig.ArgName, out object p))
                {
                    lst[i] = p;
                }
                else
                {
                    if (argConfig.DefaultValue != null)
                    {
                        lst[i] = argConfig.DefaultValue;
                    }

                    throw new ArgumentException("不存在指定参数", argConfig.ArgName);
                }
            }


            return OnExecutingInternal(funcName, lst, methodinfo);
        }

        /// <summary>
        /// 插件加载成功后触发该函数
        /// </summary>
        protected virtual void OnLoaded()
        {

        }

        /// <summary>
        /// 插件加载错误时,触发该函数
        /// </summary>
        /// <param name="error"></param>
        protected virtual void OnLoadError(Exception error)
        {
        }

        /// <summary>
        /// 插件加载前触发该函数
        /// </summary>
        protected virtual void OnLoading()
        {

        }

        private object OnExecutingInternal(string funcName, object[] args,MethodExecuteInfo methodInfo)
        {
            var result = OnCallBeforeInternal(funcName, args, methodInfo);

            if (result!= EmptyMethodFilterExecutingResult.Default)
            {
                return result;
            }

            try
            {
                result= this.CallMethod(funcName, args);

                result = OnAfterCallInternal(funcName, args, result, null, methodInfo);
            }
            catch (Exception e)
            {
                OnAfterCallInternal(funcName, args, result, e, methodInfo);
                throw;
            }

            return result;


        }

        private object OnCallBeforeInternal(string funcName, object[] args, MethodExecuteInfo methodInfo)
        {
            if (methodInfo.AOP.HasData())
            {
                var aopArgs = new MethodExecutingArgs(args);

                for (int i = 0; i < methodInfo.AOP.Length; i++)
                {
                    methodInfo.AOP[i].OnMethodExecuting(aopArgs);
                }

                if (aopArgs.Result != EmptyMethodFilterExecutingResult.Default)
                {
                    return aopArgs.Result;
                }
            }

            OnExecuting(funcName, args);

            return EmptyMethodFilterExecutingResult.Default;
        }

        private object OnAfterCallInternal(string funcName, object[] args,object result,Exception error, MethodExecuteInfo methodInfo)
        {
            OnExecuted(funcName, args, result, error);

            if (methodInfo.AOP.HasData())
            {
                for (int i = 0; i < methodInfo.AOP.Length; i++)
                {
                    var aop = methodInfo.AOP[i];

                    aop.OnMethodExecuted(args, result,error);
                }
            }

            return result;
        }

        private class ArgsInfo
        {
            public string ArgName { set; get; }

            public Type ArgsType { set; get; }

            public object DefaultValue { set; get; }
        }

        private class MethodExecuteInfo
        {
            public string RealMethodName { set; get; }

            public ArgsInfo[] Args { set; get; }

            public string ExportName { set; get; }

            public MethodFilterAttribute[] AOP { set; get; }
        }
    }
}